import fs from 'fs';
import path from 'path';
import { Context, Dependency, Module, ModuleType, Plugin, PluginLoadResult } from '../core';
import { getModuleTypeFromExtname, isJsxModule, isScriptModule, isTsModule } from '../utils';
import { ResolveType } from '../compiler';
import { Ast, ScriptAst } from '../types';
import { parse, ParserOptions } from '@babel/parser';
import traverse from '@babel/traverse';

const SCRIPT_MODULE_EXTNAME = ['.js', '.jsx', '.ts', '.tsx'];

export class ScriptPlugin implements Plugin {
  name = 'script';

  load(_context: Context, moduleId: string): PluginLoadResult | undefined {
    const extname = path.extname(moduleId);
    if (SCRIPT_MODULE_EXTNAME.includes(extname)) {
      return {
        content: fs.readFileSync(moduleId, 'utf-8'),
        moduleType: getModuleTypeFromExtname(extname),
      };
    }
    return;
  }

  parse(_context: Context, _moduleId: string, moduleType: ModuleType, content: string): Ast | undefined {
    if (isScriptModule(moduleType)) {
      // parse plugins
      const parsePlugins: ParserOptions['plugins'] = [];
      // typescript
      if (isTsModule(moduleType)) {
        parsePlugins.push('typescript');
      }
      // jsx
      if (isJsxModule(moduleType)) {
        parsePlugins.push('jsx');
      }

      // * parse
      return parse(content, {
        plugins: [...parsePlugins],
        sourceType: 'module',
      });
    }
    return;
  }

  analyzeDeps(context: Context, module: Module): Dependency[] | undefined {
    if (isScriptModule(module.type!)) {
      let deps: Dependency[] = [];
      traverse(module.ast as ScriptAst, {
        ImportDeclaration: ({ node }) => {
          deps.push({
            source: node.source.value,
            type: ResolveType.Import,
          });
        },
        ExportNamedDeclaration: ({ node }) => {
          if (node.source) {
            deps.push({
              source: node.source.value,
              type: ResolveType.ExportFrom,
            });
          }
        },
        ExportAllDeclaration: ({ node }) => {
          deps.push({
            source: node.source.value,
            type: ResolveType.ExportFrom,
          });
        },
        CallExpression: ({ node }) => {
          if (node.arguments.length !== 1) return;
          // require、import 参数都只能是一个
          if (node.callee.type === 'Identifier') {
            const arg = node.arguments[0];
            if (arg.type === 'StringLiteral') {
              if (node.callee.name === 'require') {
                deps.push({
                  source: arg.value,
                  type: ResolveType.Require,
                });
              } else if (node.callee.name === 'import') {
                deps.push({
                  source: arg.value,
                  type: ResolveType.DynamicImport,
                });
              }
            }
          }
        },
      });
      return deps;
    }
    return;
  }

  transform(context: Context, module: Module): void {
    if (isScriptModule(module.type!)) {
      // TODO:
    }
  }
}
